/*
 * Copyright (c) 2009-2010 HIT Microelectronic Center
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met: redistributions of source code must retain the above copyright
 * notice, this list of conditions and the following disclaimer;
 * redistributions in binary form must reproduce the above copyright
 * notice, this list of conditions and the following disclaimer in the
 * documentation and/or other materials provided with the distribution;
 * neither the name of the copyright holders nor the names of its
 * contributors may be used to endorse or promote products derived from
 * this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 *
 * Authors: Gou Pengfei
 *
 * Date:    Dec. 2009
 *
 */

#ifndef __CPU_BASE_EDGE_BLOCK_HH__
#define __CPU_BASE_EDGE_BLOCK_HH__

#include <bitset>
#include <vector>
#include <list>
#include <deque>
#include <queue>

#include "base/statistics.hh"
#include "base/fast_alloc.hh"
#include "base/refcnt.hh"
#include "config/the_isa.hh"
#include "arch/faults.hh"
#include "cpu/edge/comm.hh"

/**
 * BaseEdgeBlock class is the parent class of all type of Edge blocks.
 * This class is designed to hold all the static information of Edge
 * blocks like the number of insts, the number of stores and branches,
 * and the store mask, etc. Unfortunately, for distributed Edge model
 * and Atomic Edge model, different parent classes have been
 * introduced due to some design issues. I will fix this in the next
 * few weeks.
 * */
template <class Impl>
class BaseEdgeBlock : public FastAlloc, public RefCounted
{
  public:
    /** Typedefs from Impl. */
    typedef typename Impl::CPU CPU;
    typedef typename Impl::DynInstPtr DynInstPtr;
    typedef typename Impl::EdgeBlockPtr EdgeBlockPtr;

    /** Typedefs from ISA. */
    typedef TheISA::MachInst MachInst;
    typedef TheISA::ExtMachInst ExtMachInst;

    typedef typename std::list<DynInstPtr>::iterator ListIt;
    typedef typename std::list<EdgeBlockPtr>::iterator BlockListIt;
    typedef typename std::deque<DynInstPtr>::iterator QueueIt;

    /// Block ID
    typedef TheISA::BlockID BlockID;
    typedef TheISA::InstID InstID;
    typedef TheISA::HeaderInfo HeaderInfo;

    BaseEdgeBlock(BlockID blockID, CPU* cpu);

    ~BaseEdgeBlock();

  protected:

    /** Init some variables. */
    void initVars();

    /** cpu model this block point to*/
    CPU* cpu;

    /** Start address of this block*/
    Addr startAddr;

    /** Thread ID */
    ThreadID tid;

  public:

    /** A global ID of each Edge block instance. */
    BlockID blockID;

    /** Variables holding the block composition information. */
    uint32_t numUsefulInsts; /// Number of all instructions without nops and heads. 

    uint32_t numControlInsts; /// Number of control instructions.
    uint32_t numMemRefs; /// Number of all memory references. 
    uint32_t numStores; /// Number of stores. 
    uint32_t numLoads; /// Number of loads. 
    uint32_t numWrites; /// Number of writes. 
    uint32_t numReads; /// Number of register reads. 

    uint32_t numConstInsts; /// Number of constant instructions. 
    uint32_t numTestInsts; /// Number of Test instructions for predication. 
    uint32_t numReadWrites; /// Number of read/write instructions.
    uint32_t numDirectControls; /// Number of direct branches. 
    uint32_t numIndirectControls; /// Number of indirect branches.
    uint32_t numMoves; /// Number of Moves. 
    uint32_t numNullifies; /// Number of Nulls.
    uint32_t numPredOnTrues; /// Number of instructions with True tag of predication. 
    uint32_t numPredOnFalses; /// Number of instructions with False tag of predication. 

    uint32_t numExits; /// Number of exits of this block.

  protected:
    /** Flags of this block. */
    enum Flags {
        InEdgeROB, /// I'm in RoB. 
        IsSpeculative, /// I'm speculative.  
        IsCompleted,  /// I'm completed. 
        IsAcknowledged, /// What's this?
        IsCommitted, /// I'm committed. 
        IsSquashed, /// I'm squashed. 
        IsExecuting, /// I'm executing. 
        IsFault, /// I have a fault. 
        IsNop, /// I'm a nop block.
        CanCommit, /// You can commit me.
        NeedSyscall, /// I need to invoke a system call. 
        IsInCorrectPath, /// I am in the correct control path.
        NumFlags
    };

    std::bitset<NumFlags> flags;

    /**
     * This flag indicates what type of exits this hyperblock contains.
     * */
    std::bitset<TheISA::NumBranchType> typeFlags;

    /**
     * This flag indicates what type of exits this hyperblock contains,
     * including indirect types.
     * */
    std::bitset<5> completeTypeFlags;

    /** A iterator point back to the block list in cpu model which
     * hold all blocks. */
    BlockListIt blockListIt;

    /** Fault class indicate the fault caused by this block. */
    Fault fault;

  public:
    void setBlockListIt (BlockListIt it) { blockListIt = it; }
    BlockListIt &getBlockListIt() { return blockListIt; }

    void setStartPC(Addr pc);
    void setTid ( ThreadID id ) { tid = id; }
    ThreadID getTid() const { return tid;}

    Addr getStartPC();

    uint64_t getNumUsefulInst() const {return numUsefulInsts;}

    uint64_t getNumMemRefInst() const {return numMemRefs;}

    BlockID getBlockID() const {return blockID;}

    uint32_t getNumRegWriteInst() const {return numWrites; }

    uint32_t getNumRegReadInst() const {return numReads;}

    uint32_t getNumUsefulInstButMemRef() const { return (numUsefulInsts - numMemRefs); }

    uint32_t getNumStoreInst() const { return numStores; }

    uint32_t getNumLoadInst() const { return numLoads; }

    uint32_t getNumControlInst() const { return numControlInsts;}

    void setSquashed() { flags.set(IsSquashed); }

    void setExecuting() { flags.set(IsExecuting); }

    void setFault(Fault block_fault ) { fault = block_fault; flags.set(IsFault); }

    void setFault() { flags.set(IsFault); }

    void setCompleted() {
        if ( flags[IsExecuting] ) flags.reset(IsExecuting);
        flags.set(IsCompleted);
    }

    void setInEdgeROB() { flags.set(InEdgeROB); }

    void clearInEdgeROB() { flags.reset(InEdgeROB); }

    void setCommitted() { flags.set(IsCommitted); }

    void setCanCommit() { flags.set(CanCommit); }

    void setNeedSyscall() { flags.set(NeedSyscall); }

    void setInCorrectPath() { flags.set(IsInCorrectPath); }

    void setNop() { flags.set(IsNop); }

    void clearInCorrectPath() { flags.reset(IsInCorrectPath); }

    bool isInEdgeROB() const { return flags[InEdgeROB];}

    bool isSpeculative() const  { return flags[IsSpeculative]; }

    bool isNonSpeculative() const { return !flags[IsSpeculative];}

    bool isCompleted()  const { return flags[IsCompleted];}

    bool isAcknowledged()  const {return flags[IsAcknowledged]; }

    bool isSquashed() const { return flags[IsSquashed]; }

    bool isExecuting() const { return flags[IsExecuting]; }

    bool isCommitted() const { return flags[IsCommitted]; }

    bool isFault() const { return flags[IsFault]; }

    bool isNop() const { return flags[IsNop]; }

    Fault getFault() const { return fault; }

    bool readyToCommit() const { return flags[CanCommit]; }

    bool isNeedSyscall() const { return flags[NeedSyscall]; }

    bool isInCorrectPath() const { return flags[IsInCorrectPath]; }

    /** This number will not include the sequential target. */
    int getNumTypes() const { return typeFlags.count(); }

    /** This number will not include the sequential target, but indirect types
     * are included. */
    int getNumCompleteTypes() const { return completeTypeFlags.count(); }

    void setSpeculative() {
        flags.set(IsSpeculative);
    }

    void setNonSpeculative() {
        flags.reset(IsSpeculative);
    }
};

#endif //__CPU_EDGE_BLOCK_HH__
